home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
news
/
inn1.000
/
inn1.4sec-linux-src.tar
/
inn
/
innd
/
nc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-03-18
|
24KB
|
1,059 lines
/* $Revision: 1.35 $
**
** Routines for the NNTP channel. Other channels get the descriptors which
** we turn into NNTP channels, and over which we speak NNTP.
*/
#include "innd.h"
#include "dbz.h"
#define BAD_COMMAND_COUNT 10
#define WIP_CHECK (1 * 60)
#define SAVE_AMT 10
#define ART_EOF(c, s) \
((c) >= 5 && (s)[-5] == '\r' && (s)[-4] == '\n' && (s)[-3] == '.' \
&& (s)[-2] == '\r' && (s)[-1] == '\n')
/*
** An entry in the dispatch table. The name, and implementing function,
** of every command we support.
*/
typedef struct _NCDISPATCH {
STRING Name;
FUNCPTR Function;
int Size;
} NCDISPATCH;
/*
** Information about the work in progress on all our open channels.
*/
typedef struct _WIP {
char *MessageID;
long Size;
time_t Timestamp;
BUFFER Replic;
BOOL Wanted;
} WIP;
#if 0
static FUNCTYPE NCarticle();
#endif /* 0 */
static FUNCTYPE NCauthinfo();
static FUNCTYPE NChead();
static FUNCTYPE NChelp();
static FUNCTYPE NCihave();
static FUNCTYPE NClist();
static FUNCTYPE NCmode();
static FUNCTYPE NCquit();
static FUNCTYPE NCstat();
static FUNCTYPE NCxpath();
static FUNCTYPE NCxreplic();
static FUNCTYPE NC_unimp();
STATIC int NCcount; /* Number of open connections */
STATIC int NCwipsize; /* Size of NCwip array */
STATIC char *NCfreelist[5]; /* Free string list */
STATIC WIP *NCwip; /* Work-in-progress */
STATIC WIP NCnullwip;
STATIC NCDISPATCH NCcommands[] = {
#if 0
{ "article", NCarticle },
#else
{ "article", NC_unimp },
#endif /* 0 */
{ "authinfo", NCauthinfo },
{ "help", NChelp },
{ "ihave", NCihave },
{ "list", NClist },
{ "mode", NCmode },
{ "quit", NCquit },
{ "head", NChead },
{ "stat", NCstat },
{ "body", NC_unimp },
{ "group", NC_unimp },
{ "last", NC_unimp },
{ "newgroups", NC_unimp },
{ "newnews", NC_unimp },
{ "next", NC_unimp },
{ "post", NC_unimp },
{ "slave", NC_unimp },
{ "xhdr", NC_unimp },
{ "xpath", NCxpath },
{ "xreplic", NCxreplic }
};
STATIC char *NCquietlist[] = {
INND_QUIET_BADLIST
};
STATIC char NCterm[] = "\r\n";
STATIC char NCdotterm[] = ".\r\n";
STATIC char NCbadcommand[] = NNTP_BAD_COMMAND;
STATIC STRING NCgreeting;
/*
** Clear the work-in-progress entry and wake up anyone who might
** have been waiting for us.
*/
STATIC void
NCclearwip(wp)
register WIP *wp;
{
char *p;
if ((p = wp->MessageID) != NULL)
*p = '\0';
wp->Size = 0;
if (wp->Wanted) {
wp->Wanted = FALSE;
SCHANwakeup((POINTER)wp);
}
}
/*
** Write an NNTP reply message.
*/
STATIC void
NCwritetext(cp, text)
CHANNEL *cp;
char *text;
{
RCHANremove(cp);
WCHANset(cp, text, (int)strlen(text));
WCHANappend(cp, NCterm, STRLEN(NCterm));
WCHANadd(cp);
if (Tracing || cp->Tracing)
syslog(L_TRACE, "%s > %s", CHANname(cp), text);
}
/*
** Tell the NNTP channel to go away.
*/
STATIC void
NCwriteshutdown(cp, text)
CHANNEL *cp;
char *text;
{
RCHANremove(cp);
WCHANset(cp, NNTP_GOODBYE, STRLEN(NNTP_GOODBYE));
WCHANappend(cp, " ", 1);
WCHANappend(cp, text, (int)strlen(text));
WCHANappend(cp, NCterm, STRLEN(NCterm));
WCHANadd(cp);
cp->State = CSwritegoodbye;
}
/*
** If a Message-ID is bad, write a reject message and return TRUE.
*/
STATIC BOOL
NCbadid(cp, p)
register CHANNEL *cp;
register char *p;
{
if (ARTidok(p))
return FALSE;
NCwritetext(cp, NNTP_HAVEIT_BADID);
syslog(L_NOTICE, "%s bad_messageid %s", CHANname(cp), MaxLength(p, p));
return TRUE;
}
/*
** We have an entire article collected; try to post it. If we're
** not running, drop the article or just pause and reschedule.
*/
STATIC void
NCpostit(cp)
register CHANNEL *cp;
{
STRING response;
WIP *wp;
/* Note that some use break, some use return here. */
switch (Mode) {
default:
syslog(L_ERROR, "%s internal NCpostit mode %d", CHANname(cp), Mode);
return;
case OMpaused:
SCHANadd(cp, (time_t)(Now.time + PAUSE_RETRY_TIME), (POINTER)&Mode,
NCpostit, (POINTER)NULL);
return;
case OMrunning:
wp = &NCwip[cp->fd];
response = ARTpost(cp, AmSlave ? &wp->Replic : NULL, wp->MessageID);
if (atoi(response) == NNTP_TOOKIT_VAL)
cp->Received++;
else
cp->Rejected++;
cp->Reported++;
if (cp->Reported >= NNTP_ACTIVITY_SYNC) {
syslog(L_NOTICE,
"%s checkpoint seconds %ld accepted %ld refused %ld rejected %ld",
CHANname(cp), (long)(Now.time - cp->Started),
cp->Received, cp->Refused, cp->Rejected);
cp->Reported = 0;
}
NCwritetext(cp, response);
cp->State = CSgetcmd;
break;
case OMthrottled:
NCwriteshutdown(cp, ModeReason);
cp->Rejected++;
break;
}
/* Clear the work-in-progress entry. */
NCclearwip(&NCwip[cp->fd]);
}
/*
** Write-done function. Close down or set state for what we expect to
** read next.
*/
STATIC FUNCTYPE
NCwritedone(cp)
register CHANNEL *cp;
{
switch (cp->State) {
default:
syslog(L_ERROR, "%s internal NCwritedone state %d",
CHANname(cp), cp->State);
break;
case CSwritegoodbye:
if (NCcount > 0)
NCcount--;
CHANclose(cp, CHANname(cp));
break;
case CSgetcmd:
case CSgetauth:
case CSgetarticle:
case CSgetrep:
RCHANadd(cp);
break;
}
}
#if 0
/*
** The "article" command.
*/
STATIC FUNCTYPE
NCarticle(cp)
register CHANNEL *cp;
{
register char *p;
register char *q;
char *art;
/* Snip off the Message-ID. */
for (p = cp->In.Data + STRLEN("article"); ISWHITE(*p); p++)
continue;
if (NCbadid(cp, p))
return;
/* Get the article filenames, and the article header+body. */
if ((art = ARTreadarticle(HISfilesfor(p))) == NULL) {
NCwritetext(cp, NNTP_DONTHAVEIT);
return;
}
/* Write it. */
NCwritetext(cp, NNTP_ARTICLE_FOLLOWS);
for (p = art; ((q = strchr(p, '\n')) != NULL); p = q + 1) {
if (*p == '.')
WCHANappend(cp, ".", 1);
WCHANappend(cp, p, q - p);
WCHANappend(cp, NCterm, STRLEN(NCterm));
}
/* Write the terminator. */
WCHANappend(cp, NCdotterm, STRLEN(NCdotterm));
}
#endif /* 0 */
/*
** The "head" command.
*/
STATIC FUNCTYPE
NChead(cp)
CHANNEL *cp;
{
register char *p;
register char *q;
char *head;
/* Snip off the Message-ID. */
for (p = cp->In.Data + STRLEN("head"); ISWHITE(*p); p++)
continue;
if (NCbadid(cp, p))
return;
/* Get the article filenames, and the header. */
if ((head = ARTreadheader(HISfilesfor(p))) == NULL) {
NCwritetext(cp, NNTP_DONTHAVEIT);
return;
}
/* Write it. */
NCwritetext(cp, NNTP_HEAD_FOLLOWS);
for (p = head; ((q = strchr(p, '\n')) != NULL); p = q + 1) {
if (*p == '.')
WCHANappend(cp, ".", 1);
WCHANappend(cp, p, q - p);
WCHANappend(cp, NCterm, STRLEN(NCterm));
}
/* Write the terminator. */
WCHANappend(cp, NCdotterm, STRLEN(NCdotterm));
}
/*
** The "stat" command.
*/
STATIC FUNCTYPE
NCstat(cp)
CHANNEL *cp;
{
register char *p;
char buff[SMBUF];
/* Snip off the Message-ID. */
for (p = cp->In.Data + STRLEN("stat"); ISWHITE(*p); p++)
continue;
if (NCbadid(cp, p))
return;
/* Get the article filenames; read the header (to make sure not
* the article is still here). */
if (ARTreadheader(HISfilesfor(p)) == NULL) {
NCwritetext(cp, NNTP_DONTHAVEIT);
return;
}
/* Write the message. */
(void)sprintf(buff, "%d 0 %s", NNTP_NOTHING_FOLLOWS_VAL, p);
NCwritetext(cp, buff);
}
/*
** The "authinfo" command. Actually, we come in here whenever the
** channel is in CSgetauth state and we just got a command.
*/
STATIC FUNCTYPE
NCauthinfo(cp)
register CHANNEL *cp;
{
static char AUTHINFO[] = "authinfo ";
static char PASS[] = "pass ";
static char USER[] = "user ";
register char *p;
p = cp->In.Data;
/* Allow the poor sucker to quit. */
if (caseEQ(p, "quit")) {
NCquit(cp);
return;
}
/* Otherwise, make sure we're only getting "authinfo" commands. */
if (!caseEQn(p, AUTHINFO, STRLEN(AUTHINFO))) {
NCwritetext(cp, NNTP_AUTH_NEEDED);
return;
}
for (p += STRLEN(AUTHINFO); ISWHITE(*p); p++)
continue;
/* Ignore "authinfo user" commands, since we only care about the
* password. */
if (caseEQn(p, USER, STRLEN(USER))) {
NCwritetext(cp, NNTP_AUTH_NEXT);
return;
}
/* Now make sure we're getting only "authinfo pass" commands. */
if (!caseEQn(p, PASS, STRLEN(PASS))) {
NCwritetext(cp, NNTP_AUTH_NEEDED);
return;
}
for (p += STRLEN(PASS); ISWHITE(*p); p++)
continue;
/* Got the password -- is it okay? */
if (!RCauthorized(cp, p)) {
NCwritetext(cp, NNTP_AUTH_BAD);
cp->State = CSwritegoodbye;
}
else {
NCwritetext(cp, NNTP_AUTH_OK);
cp->State = CSgetcmd;
}
}
/*
** Is someone already sending us this article?
*/
STATIC BOOL
NCinprogress(cp, id, who)
CHANNEL *cp;
register char *id;
WIP **who;
{
register WIP *wp;
register char *p;
register int i;
for (i = NCwipsize, wp = NCwip; --i >= 0; wp++)
if ((p = wp->MessageID) != NULL && *p == *id && EQ(p, id)
&& Now.time - wp->Timestamp < WIP_CHECK) {
*who = wp;
return TRUE;
}
wp = &NCwip[cp->fd];
if (wp->MessageID == NULL) {
for (i = SIZEOF(NCfreelist); --i >= 0; )
if (NCfreelist[i] != NULL) {
wp->MessageID = NCfreelist[i];
NCfreelist[i] = NULL;
break;
}
if (i < 0)
wp->MessageID = NEW(char, DBZMAXKEY + 3);
}
(void)strcpy(wp->MessageID, id);
return FALSE;
}
/*
** The "help" command.
*/
STATIC FUNCTYPE
NChelp(cp)
register CHANNEL *cp;
{
static char LINE1[] = "For more information, contact \"";
static char LINE2[] = "\" at this machine.";
register NCDISPATCH *dp;
NCwritetext(cp, NNTP_HELP_FOLLOWS);
for (dp = NCcommands; dp < ENDOF(NCcommands); dp++)
if (dp->Function != NC_unimp) {
WCHANappend(cp, "\t", 1);
WCHANappend(cp, dp->Name, dp->Size);
WCHANappend(cp, NCterm, STRLEN(NCterm));
}
WCHANappend(cp, LINE1, STRLEN(LINE1));
WCHANappend(cp, NEWSMASTER, STRLEN(NEWSMASTER));
WCHANappend(cp, LINE2, STRLEN(LINE2));
WCHANappend(cp, NCterm, STRLEN(NCterm));
WCHANappend(cp, NCdotterm, STRLEN(NCdotterm));
}
#if !defined(NNTP_RESENDIT_LATER)
/*
** We woke up because we got offered an article that was already in
** progress somewhere else. If the other channel finished, then we
** don't want the article, otherwise let's accept it.
*/
STATIC FUNCTYPE
NCwaitfor(cp)
register CHANNEL *cp;
{
WIP *who;
if (HIShavearticle(cp->Argument)) {
NCwritetext(cp, NNTP_HAVEIT);
DISPOSE(cp->Argument);
cp->Argument = NULL;
}
else if (NCinprogress(cp, cp->Argument, &who)) {
who->Wanted = TRUE;
SCHANadd(cp, (time_t)(Now.time + WIP_CHECK / 2 + 1), (POINTER)who,
NCwaitfor, (POINTER)cp->Argument);
}
else {
NCwritetext(cp, NNTP_SENDIT);
cp->State = CSgetarticle;
DISPOSE(cp->Argument);
cp->Argument = NULL;
}
}
#endif /* !defined(NNTP_RESENDIT_LATER) */
/*
** The "ihave" command. Check the Message-ID, and see if we want the
** article or not. Set the state appropriately.
*/
STATIC FUNCTYPE
NCihave(cp)
CHANNEL *cp;
{
register char *p;
WIP *who;
if (AmSlave) {
NCwritetext(cp, NCbadcommand);
return;
}
/* Snip off the Message-ID. */
for (p = cp->In.Data + STRLEN("ihave"); ISWHITE(*p); p++)
continue;
if (NCbadid(cp, p))
return;
if (HIShavearticle(p)) {
cp->Refused++;
NCwritetext(cp, NNTP_HAVEIT);
}
else if (NCinprogress(cp, p, &who)) {
#if defined(NNTP_RESENDIT_LATER)
NCwritetext(cp, NNTP_RESENDIT_LATER);
#else
/* Somebody else is sending it to us; wait until they're done. */
who->Wanted = TRUE;
SCHANadd(cp, (time_t)(Now.time + WIP_CHECK + 1), (POINTER)who,
NCwaitfor, (POINTER)COPY(p));
/* Clear input buffer. */
cp->In.Used = 0;
#endif /* defined(NNTP_RESENDIT_LATER) */
}
else {
NCwritetext(cp, NNTP_SENDIT);
cp->State = CSgetarticle;
}
}
/*
** The "list" command. Send the active file.
*/
STATIC FUNCTYPE
NClist(cp)
register CHANNEL *cp;
{
register char *p;
register char *q;
char *trash;
char *end;
for (p = cp->In.Data + STRLEN("list"); ISWHITE(*p); p++)
continue;
if (caseEQ(p, "newsgroups")) {
trash = p = ReadInFile(_PATH_NEWSGROUPS, (struct stat *)NULL);
end = p + strlen(p);
}
else if (caseEQ(p, "active.times")) {
trash = p = ReadInFile(_PATH_ACTIVETIMES, (struct stat *)NULL);
end = p + strlen(p);
}
else if (*p == '\0' || (caseEQ(p, "active"))) {
p = ICDreadactive(&end);
trash = NULL;
}
else {
NCwritetext(cp, NCbadcommand);
return;
}
/* Loop over all lines, sending the text and \r\n. */
NCwritetext(cp, NNTP_LIST_FOLLOWS);
for (; p < end && (q = strchr(p, '\n')) != NULL; p = q + 1) {
WCHANappend(cp, p, q - p);
WCHANappend(cp, NCterm, STRLEN(NCterm));
}
WCHANappend(cp, NCdotterm, STRLEN(NCdotterm));
if (trash)
DISPOSE(trash);
}
/*
** The "mode" command. Hand off the channel.
*/
STATIC FUNCTYPE
NCmode(cp)
register CHANNEL *cp;
{
register char *p;
HANDOFF h;
/* Skip the first word, get the argument. */
for (p = cp->In.Data + STRLEN("mode"); ISWHITE(*p); p++)
continue;
if (caseEQ(p, "reader"))
h = HOnnrpd;
else if (caseEQ(p, "query"))
h = HOnnrqd;
else {
NCwritetext(cp, NCbadcommand);
return;
}
RChandoff(cp->fd, h);
if (NCcount > 0)
NCcount--;
CHANclose(cp, CHANname(cp));
}
/*
** The "quit" command. Acknowledge, and set the state to closing down.
*/
STATIC FUNCTYPE
NCquit(cp)
CHANNEL *cp;
{
register WIP *wp;
register int i;
wp = &NCwip[cp->fd];
for (i = SIZEOF(NCfreelist); --i >= 0; )
if (NCfreelist[i] == NULL) {
NCfreelist[i] = wp->MessageID;
wp->MessageID = NULL;
break;
}
#if 0
if (i < 0) {
DISPOSE(wp->MessageID);
wp->MessageID = NULL;
}
#endif /* 0 */
NCwritetext(cp, NNTP_GOODBYE_ACK);
cp->State = CSwritegoodbye;
}
/*
** The "xpath" command. Return the paths for an article is.
*/
STATIC FUNCTYPE
NCxpath(cp)
CHANNEL *cp;
{
static BUFFER Reply;
register char *p;
register int i;
/* Nip off the Message-ID. */
for (p = cp->In.Data + STRLEN("xpath"); ISWHITE(*p); p++)
continue;
if (NCbadid(cp, p))
return;
if ((p = HISfilesfor(p)) == NULL) {
NCwritetext(cp, NNTP_DONTHAVEIT);
return;
}
i = 3 + 1 + strlen(p);
if (Reply.Data == NULL) {
Reply.Size = i;
Reply.Data = NEW(char, i + 1);
}
else if (Reply.Size < i) {
Reply.Size = i;
RENEW(Reply.Data, char, i + 1);
}
(void)sprintf(Reply.Data, "%d %s", NNTP_NOTHING_FOLLOWS_VAL, p);
NCwritetext(cp, Reply.Data);
}
/*
** The "xreplic" command. Take an article and the places to file it.
*/
STATIC FUNCTYPE
NCxreplic(cp)
CHANNEL *cp;
{
register char *p;
register BUFFER *bp;
register int i;
if (!RCismaster(cp->Address)) {
NCwritetext(cp, NCbadcommand);
return;
}
/* Stash the filename arguments. */
for (p = cp->In.Data + STRLEN("xreplic"); ISWHITE(*p); p++)
continue;
i = cp->In.Used - (p - cp->In.Data) + 1;
bp = &NCwip[cp->fd].Replic;
if (bp->Data == NULL) {
bp->Size = i;
bp->Data = NEW(char, i);
}
BUFFset(bp, p, i);
bp->Used = bp->Left;
/* Tell master to send it to us. */
NCwritetext(cp, NNTP_SENDIT);
cp->State = CSgetrep;
}
/*
** The catch-all for inimplemented commands.
*/
STATIC FUNCTYPE
NC_unimp(cp)
CHANNEL *cp;
{
register char *p;
char buff[SMBUF];
/* Nip off the first word. */
for (p = cp->In.Data; *p && !ISWHITE(*p); p++)
continue;
*p = '\0';
(void)sprintf(buff, "%d \"%s\" not implemented; try \"help\".",
NNTP_BAD_COMMAND_VAL, MaxLength(cp->In.Data, cp->In.Data));
NCwritetext(cp, buff);
}
/*
** Remove the \r\n and leading dot escape that the NNTP protocol adds.
*/
STATIC void
NCclean(bp)
register BUFFER *bp;
{
register char *end;
register char *p;
register char *dest;
for (p = bp->Data, dest = p, end = p + bp->Used; p < end; ) {
if (p[0] == '\r' && p[1] == '\n') {
p += 2;
*dest++ = '\n';
if (p[0] == '.' && p[1] == '.') {
p += 2;
*dest++ = '.';
}
}
else
*dest++ = *p++;
}
*dest = '\0';
bp->Used = dest - bp->Data;
}
/*
** Read whatever data is available on the channel. If we got the
** full amount (i.e., the command or the whole article) process it.
*/
STATIC FUNCTYPE
NCreader(cp)
register CHANNEL *cp;
{
register char *p;
register NCDISPATCH *dp;
register BUFFER *bp;
register WIP *wp;
STRING q;
char buff[SMBUF];
char *av[2];
int i;
/* Read any data that's there; ignore errors (retry next time it's our
* turn) and if we got nothing, then it's EOF so mark it closed. */
if ((i = CHANreadtext(cp)) < 0) {
if (cp->BadReads++ >= BAD_IO_COUNT) {
if (NCcount > 0)
NCcount--;
CHANclose(cp, CHANname(cp));
}
return;
}
if (i == 0) {
NCcount--;
return;
}
/* Update timestamp. */
wp = &NCwip[cp->fd];
wp->Timestamp = Now.time;
bp = &cp->In;
p = &bp->Data[bp->Used];
switch (cp->State) {
default:
syslog(L_ERROR, "%s internal NCreader state %d",
CHANname(cp), cp->State);
break;
case CSgetcmd:
case CSgetauth:
/* Did we get the whole command, terminated with "\r\n"? */
if (bp->Used < 2 || p[-2] != '\r' || p[-1] != '\n')
break;
p[-2] = '\0';
bp->Used -= 2;
/* Ignore blank lines. */
if (bp->Used == 0)
break;
if (Tracing || cp->Tracing)
syslog(L_TRACE, "%s < %s", CHANname(cp), bp->Data);
/* We got something -- stop sleeping (in case we were). */
SCHANremove(cp);
if (cp->Argument != NULL) {
DISPOSE(cp->Argument);
cp->Argument = NULL;
}
if (cp->State == CSgetauth) {
if (caseEQn(bp->Data, "mode", 4))
NCmode(cp);
else
NCauthinfo(cp);
break;
}
/* Loop through the command table. */
for (p = bp->Data, dp = NCcommands; dp < ENDOF(NCcommands); dp++)
if (caseEQn(p, dp->Name, dp->Size)) {
(*dp->Function)(cp);
cp->BadCommands = 0;
break;
}
if (dp == ENDOF(NCcommands)) {
NCwritetext(cp, NCbadcommand);
if (++(cp->BadCommands) >= BAD_COMMAND_COUNT)
cp->State = CSwritegoodbye;
for (i = 0; (p = NCquietlist[i]) != NULL; i++)
if (caseEQ(p, dp->Name))
break;
if (p == NULL)
syslog(L_NOTICE, "%s bad_command %s",
CHANname(cp), MaxLength(bp->Data, bp->Data));
}
break;
case CSgetarticle:
case CSgetrep:
/* Reading an article; look for "\r\n.\r\n" terminator. */
if (!ART_EOF(bp->Used, p)) {
/* Check for the null article. */
if (bp->Used == 3
&& p[-3] == '.' && p[-2] == '\r' && p[-1] == '\n') {
cp->Rejected++;
NCwritetext(cp, NNTP_REJECTIT_EMPTY);
cp->State = CSgetcmd;
bp->Used = 0;
/* Clear the work-in-progress entry. */
NCclearwip(wp);
}
/* Check for big articles. */
if (LargestArticle > SAVE_AMT && bp->Used > LargestArticle) {
/* Make some room, saving only the last few bytes. */
for (p = bp->Data, i = 0; i < SAVE_AMT; p++, i++)
p[0] = p[bp->Used - SAVE_AMT];
wp->Size += bp->Used - SAVE_AMT;
bp->Used = SAVE_AMT;
cp->State = CSeatarticle;
}
break;
}
/* Strip article terminator and post the article. */
p[-3] = '\0';
bp->Used -= 3;
SCHANremove(cp);
if (cp->Argument != NULL) {
DISPOSE(cp->Argument);
cp->Argument = NULL;
}
NCclean(bp);
NCpostit(cp);
break;
case CSeatarticle:
/* Eat the article and then complain that it was too large */
if (ART_EOF(bp->Used, p)) {
/* Reached the end of the article. */
SCHANremove(cp);
if (cp->Argument != NULL) {
DISPOSE(cp->Argument);
cp->Argument = NULL;
}
p = wp->MessageID;
i = wp->Size + bp->Used;
syslog(L_ERROR, "%s internal rejecting huge article %s (%d > %d)",
CHANname(cp), p ? p : "(null)", i, LargestArticle);
(void)sprintf(buff, "%d Article exceeds local limit of %ld bytes",
NNTP_REJECTIT_VAL, LargestArticle);
NCwritetext(cp, buff);
cp->State = CSgetcmd;
cp->Rejected++;
/* Write a local cancel entry so nobody else gives it to us. */
if (p) {
av[0] = p;
av[1] = NULL;
if ((q = CCcancel(av)) != NULL)
syslog(L_ERROR, "%s cant cancel %s %s", LogName, av[0], q);
}
/* Clear the work-in-progress entry. */
NCclearwip(wp);
/* Reset input buffer to the default size; don't let realloc
* be lazy. */
DISPOSE(bp->Data);
bp->Size = START_BUFF_SIZE;
bp->Used = 0;
bp->Data = NEW(char, bp->Size);
}
else if (bp->Used > 8 * 1024) {
/* Make some room; save the last few bytes of the article */
for (p = bp->Data, i = 0; i < SAVE_AMT; p++, i++)
p[0] = p[bp->Used - SAVE_AMT + 0];
wp->Size += bp->Used - SAVE_AMT;
bp->Used = SAVE_AMT;
}
break;
}
}
/*
** Set up the NNTP channel state.
*/
void
NCsetup(i)
register int i;
{
register WIP *wp;
register NCDISPATCH *dp;
char *p;
char buff[SMBUF];
/* Set the greeting message. */
if ((p = GetConfigValue(_CONF_PATHHOST)) == NULL)
/* Worked in main, now it fails? Curious. */
p = Path.Data;
(void)sprintf(buff, "%d %s InterNetNews server %s ready",
NNTP_POSTOK_VAL, p, Version);
NCgreeting = COPY(buff);
/* Set up the work-in-progress structure. */
for (wp = NCwip = NEW(WIP, i), NCwipsize = i; --i >= 0; wp++)
*wp = NCnullwip;
/* Get the length of every command. */
for (dp = NCcommands; dp < ENDOF(NCcommands); dp++)
dp->Size = strlen(dp->Name);
}
/*
** Tear down our state.
*/
void
NCclose()
{
register WIP *wp;
register int i;
register CHANNEL *cp;
int j;
/* Close all incoming channels. */
for (j = 0; (cp = CHANiter(&j, CTnntp)) != NULL; ) {
if (NCcount > 0)
NCcount--;
CHANclose(cp, CHANname(cp));
}
/* Free the WIP list. */
for (wp = NCwip, i = NCwipsize; --i >= 0; wp++) {
if (wp->MessageID)
DISPOSE(wp->MessageID);
if (wp->Replic.Data)
DISPOSE(wp->Replic.Data);
}
DISPOSE(NCwip);
for (i = SIZEOF(NCfreelist); --i >= 0; )
if (NCfreelist[i] != NULL)
DISPOSE(NCfreelist[i]);
}
/*
** Create an NNTP channel and print the greeting message.
*/
CHANNEL *
NCcreate(fd, MustAuthorize)
int fd;
BOOL MustAuthorize;
{
register CHANNEL *cp;
int i;
/* Create the channel. */
cp = CHANcreate(fd, CTnntp, MustAuthorize ? CSgetauth : CSgetcmd,
NCreader, NCwritedone);
NCclearwip(&NCwip[cp->fd]);
#if defined(SOL_SOCKET) && defined(SO_SNDBUF) && defined(SO_RCVBUF)
i = 24 * 1024;
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&i, sizeof i) < 0)
syslog(L_ERROR, "%s cant setsockopt(SNDBUF) %m", CHANname(cp));
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&i, sizeof i) < 0)
syslog(L_ERROR, "%s cant setsockopt(RCVBUF) %m", CHANname(cp));
#endif /* defined(SOL_SOCKET) && defined(SO_SNDBUF) && defined(SO_RCVBUF) */
/* Now check our operating mode. */
NCcount++;
if (Mode == OMthrottled) {
NCwriteshutdown(cp, ModeReason);
return cp;
}
if (RejectReason) {
NCwriteshutdown(cp, RejectReason);
return cp;
}
/* See if we have too many channels. */
if (MaxIncoming && NCcount >= MaxIncoming && !RCnolimit(cp)) {
/* Recount, just in case we got out of sync. */
for (NCcount = 0, i = 0; CHANiter(&i, CTnntp) != NULL; )
NCcount++;
if (NCcount >= MaxIncoming) {
NCwriteshutdown(cp, "Too many connections");
return cp;
}
}
cp->BadReads = 0;
cp->BadCommands = 0;
NCwritetext(cp, NCgreeting);
return cp;
}